<template>
  <div id="webgl"></div>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";
import * as THREE from "three";

// 创建3D场景对象Scene
const scene: THREE.Scene = new THREE.Scene();

// 球
const sphere: THREE.Mesh = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshStandardMaterial()
);
sphere.castShadow = true; //开启阴影投射
scene.add(sphere);

// 平面
const plane: THREE.Mesh = new THREE.Mesh(
  new THREE.PlaneGeometry(10, 10),
  new THREE.MeshStandardMaterial()
);
plane.receiveShadow = true; //开启阴影接收
plane.position.y = -1;
plane.rotation.x = -Math.PI / 2;
scene.add(plane);

const gui: GUI = new GUI();
// 灯光
// 直线光源
/*
const directionalLight: THREE.DirectionalLight = new THREE.DirectionalLight(
  0xffffff,
  0.5
);
directionalLight.position.set(10, 10, 10);
directionalLight.castShadow = true; //开启阴影投射
scene.add(directionalLight);

directionalLight.shadow.radius = 20; //阴影模糊度
directionalLight.shadow.mapSize.width = 2048; //阴影贴图宽度
directionalLight.shadow.mapSize.height = 2048; //阴影贴图高度
directionalLight.shadow.camera.near = 0.5; //产生阴影的最近距离
directionalLight.shadow.camera.far = 500; //产生阴影的最远距离
directionalLight.shadow.camera.left = -5; //产生阴影距离位置的最左边位置
directionalLight.shadow.camera.right = 5; //最右边
directionalLight.shadow.camera.top = 5; //最上边
directionalLight.shadow.camera.bottom = -5; //最下面

gui
  .add(directionalLight.shadow.camera, "near")
  .min(0)
  .max(20)
  .step(0.1)
  .onChange(function () {
    directionalLight.shadow.camera.updateProjectionMatrix(); //更新投影矩阵
    renderer.render(scene, camera); //执行渲染操作
  });
*/

// 聚光灯
/*
const spotLight: THREE.SpotLight = new THREE.SpotLight(0xffffff, 0.5);
spotLight.decay = 0;
spotLight.position.set(10, 10, 10);
spotLight.castShadow = true; //开启阴影投射
spotLight.target = sphere;
spotLight.angle = Math.PI / 6;
scene.add(spotLight);

gui
  .add(sphere.position, "x")
  .min(0)
  .max(Math.PI / 2)
  .step(0.01)
  .onChange(function () {
    renderer.render(scene, camera);
  });
gui
  .add(spotLight, "angle")
  .min(-5)
  .max(5)
  .step(0.01)
  .onChange(function () {
    renderer.render(scene, camera);
  });
gui
  .add(spotLight, "distance")
  .min(0)
  .max(30)
  .step(0.01)
  .onChange(function () {
    renderer.render(scene, camera);
  });
gui
  .add(spotLight, "penumbra")
  .min(0)
  .max(1)
  .step(0.01)
  .onChange(function () {
    renderer.render(scene, camera);
  });
gui
  .add(spotLight, "decay")
  .min(0)
  .max(5)
  .step(0.01)
  .onChange(function () {
    renderer.render(scene, camera);
  });
  */

// 点光源
const pointLight: THREE.PointLight = new THREE.PointLight(0xff0000, 1);
// pointLight.position.set(2, 2, 2);
pointLight.castShadow = true; //开启阴影投射
// scene.add(pointLight);

// 发光小球
const smallBall: THREE.Mesh = new THREE.Mesh(
  new THREE.SphereGeometry(0.2, 32, 32),
  new THREE.MeshBasicMaterial({ color: 0xff0000 })
);

scene.add(smallBall);
smallBall.add(pointLight);
smallBall.position.set(2, 2, 2);

// 定义threejs输出画布的尺寸(单位:像素px)
const width = window.innerWidth; //宽度
const height = window.innerHeight; //高度
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(10, 10, 10);
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(sphere.position); //指向mesh对应的位置

// AxesHelper：辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();

renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
// 设置阴影贴图
renderer.shadowMap.enabled = true;
renderer.render(scene, camera);

// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数，重新调用渲染器渲染三维场景
controls.addEventListener("change", function () {
  renderer.render(scene, camera); //执行渲染操作
}); //监听鼠标、键盘事件

// 设置时钟, 圆周运动
const clock: THREE.Clock = new THREE.Clock();
function render() {
  let time: number = clock.getElapsedTime();
  smallBall.position.x = Math.sin(time) * 3;
  smallBall.position.z = Math.cos(time) * 3;
  controls.update(); //执行监听
  renderer.render(scene, camera); //执行渲染操作
  requestAnimationFrame(render);
}

render();

onMounted(() => {
  (document.getElementById("webgl") as HTMLElement).appendChild(
    renderer.domElement
  );
});
</script>
